Quiz (Week 8)
Table of Contents
1 IO Monad i
1.1 Question 1
What does the type IO String
signify?
- ✗A procedure that, when carried out, may involve input and output, before eventually returning a
String
. - ✗A function from the abstract state of the
RealWorld
to a pair of theRealWorld
state and anString
. - ✗An effectful program that, when executed, produces an
String
. - ✔All of the above views are valid interpretations
GHC internally models IO a
similarly to the following type:
IO a =~ RealWorld -> (RealWorld, a)
This is a common view of IO
and option 2. Perhaps an even more common view of IO
is that
it denotes procedure. For example,
getChar :: IO Char
could be viewed as a type representing a procedure that will produce a Char
when it's carried out. This is a very intuitive way of thinking about IO
: a function of
type String -> IO ()
, such as putStrLn
, takes a String
and returns a procedure
that, when carried out, results in the given string being printed. Keep in mind that the
procedure is not carried out inside the Haskell program: rather, it's carried out when
the program is executed.
1.2 Question 2
Imagine we had the following IO
based API for manipulating a robot:
data Direction = L | R forward :: IO () obstructed :: IO Bool turn :: Direction -> IO ()
We wish to write a program that will move forward
unless obstructed
, in which case
the robot should turn
towards the L
direction.
Which of the following is a type-correct implementation of the above procedure?
- ✔
robot = do sensed <- obstructed if sensed then turn L else forward robot
- ✗
robot = do if obstructed then turn L else forward robot
- ✗
robot = do let sensed = obstructed if sensed then turn L else forward robot
- ✗
robot = do sensed <- obstructed if sensed then turn L robot else forward robot
Option 1 is correct. Option 2 uses an IO Bool
(obstructed
) where a Bool
is required (in the if
).
Option 3 uses let
to bind sensed
to obstructed
. That is, sensed
now has type IO Bool
, which
once again is incorrectly used within the if
. Option 4 places the robot
looping call at the same indentation
as turn L
and forward
, but without the do
keyword they do not form a block and so Haskell would
parse this as turn L robot
which is not well-typed.
1.3 Question 3
Check all of the following programs that are equivalent to the IO
action b
:
b = do x <- getLine putStrLn (filter (not . isDigit) x) b
- ✔
a = getLine >>= putStrLn . filter (not . isDigit) >> a
- ✔
a = getLine >>= \x -> putStrLn (filter (not . isDigit) x) >>= \_ -> a
- ✗
a = (getLine >>= \x -> putStrLn (filter (not . isDigit) x)) >>= a
- ✗
a = do getLine >>= \x -> putStrLn . filter (not . isDigit); a
- ✗
a = do x <- getLine; putStrLn . filter (not . isDigit); a
- ✔
a = do x <- getLine; putStrLn . filter (not . isDigit) $ x; a
- ✔
a = do getLine >>= \x -> putStrLn (filter (not . isDigit) x); a
Options 3,4, and 5 don't type-check. The others are all equivalent.
1.4 Question 4
Which of the following defines a function that takes a String
as its argument and
causes it to be written to the standard output, given that putChar :: Char -> IO ()
takes a character as its parameter and writes it to the standard output.
- ✗
f [] = return "" f (x:xs) = putChar x >>= \_ -> xs
- ✔
f [] = return () f (x:xs) = putChar x >>= \_ -> f xs
- ✗
f [] = return () f (x:xs) = putChar x >>= \xs -> f (show xs)
- ✗
f xs = case xs of [] -> return () (x:xs) -> f xs >>= \_ -> putChar x
The first and fourth are not even type-correct. The third is, but it's not terminating. The second implementation is correct.
1.5 Question 5
Pick all possible implementations that define a function
mapIO :: (a -> b) -> IO a -> IO b
that takes a function of
type a -> b
and "maps" it over a non-bottom monadic value of type
IO a
to produce a value of type IO b
?
- ✗
mapIO f m = return (f m)
- ✔
mapIO f m = m >>= \x -> return (f x)
- ✗
mapIO f m = m >>= \x -> f x
- ✔
mapIO f = (f <$>)
The first and third answers do not type-check. In the second implementation,
we have that m
has type IO a
, so the variable x
has type a
. Therefore,
the variable f x
has type b
, and return (f x)
has type IO b
as desired.
The fourth answer works by using the fact that all Monad~s are ~Applicative
.
1.6 Question 6
Which of the following types can a total/non-partial terminating function never have?
- ✗
a :: IO (a -> b) -> IO a -> IO b
- ✗
b :: IO (a -> b) -> a -> IO b
- ✔
c :: IO (a -> b) -> (a -> b)
- ✗
d :: a -> IO a
The first function, a
is just a = fmap
, using the fact that every Monad
, including IO
, is a Functor
.
Similarly, the third function, d
is just d = return
.
The second function can be implemented as follows:
b :: IO (a -> b) -> a -> IO b b f x = f >>= \f' -> return (f' x)
This leaves the third function, which cannot be implemented: if we had such an implementation c
,
we could use that c
to also implement the following function:
evalIO :: IO a -> a evalIO p = (p >>= \x -> c (return (const x))) ()
but we know from the lecture that evaluating IO
like this is impossible.
2 List Comprehensions
2.1 Question 7
Which of the following expressions evaluates to the sum of the first 100 square numbers?
- ✗
foldl (++) [] [[x * x] | x <- [1..100]]
- ✔
sum [x^2 | x <- [1..100]]
- ✗
sum [const x x | x <- [1..100]]
- ✔
sum (map (^2) [1..100])
The first expression does not even type-check. The second is correct, as is the fourth.
The third one calculates the sum of the first 100 integers, since const x x == x
.
#+ENDSRC
2.2 Question 8
Choose the expression equivalent to the list comprehension [f x | x <- xs, phi x]
!
- ✗
map f (map phi xs)
- ✗
filter phi (map f xs)
- ✔
map f (filter phi xs)
- ✗
map phi (filter f xs)
Only the third choice is type-correct.